package io.kiki.sba.registry.util;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;


public final class ConcurrentUtils {
    private static final Logger logger = LoggerFactory.getLogger(ConcurrentUtils.class);

    private ConcurrentUtils() {
    }

    public static Thread createDaemonThread(String name, Runnable runnable) {
        Thread thread = new Thread(runnable, name);
        thread.setDaemon(true);
        return thread;
    }

    public static void objectWaitUninterruptibly(Object o, int timeoutMs) {
        try {
            o.wait(timeoutMs);
        } catch (InterruptedException ignored) {
            // no need to remark Thread.currentThread().interrupt();
            logger.warn("Interrupted waiting", ignored);
        }
    }

    public static void sleepUninterruptibly(long sleepFor, TimeUnit timeUnit) {
        try {
            timeUnit.sleep(sleepFor);
        } catch (InterruptedException ignored) {
            // no need to remark Thread.currentThread().interrupt();
            logger.warn("Interrupted sleeping", ignored);
        }
    }

    public static <T> T pollUninterruptibly(BlockingQueue<T> queue, long wait, TimeUnit timeUnit) {
        try {
            return queue.poll(wait, timeUnit);
        } catch (InterruptedException ignored) {
            // no need to remark Thread.currentThread().interrupt();
            logger.warn("Interrupted polling", ignored);
        }
        return null;
    }

    public abstract static class SafeParaLoop<T> {

        private static final Logger logger = LoggerFactory.getLogger(SafeParaLoop.class);

        private final List<T> list;

        private final Executor executor;

        public SafeParaLoop(Executor executor, Collection<T> collection) {
            this.executor = executor;
            this.list = collection == null ? Collections.emptyList() : new LinkedList<>(collection);
        }

        public void run() {
            for (T t : list) {
                executor.execute(() -> {
                    try {
                        doRun0(t);
                    } catch (Throwable e) {
                        logger.error("[SafeParaLoop][{}]", t, e);
                    }
                });
            }
        }

        public boolean runAndWait(long timeoutMillis) {
            CountDownLatch countDownLatch = new CountDownLatch(list.size());
            for (T t : list) {
                executor.execute(() -> {
                    try {
                        doRun0(t);
                    } catch (Throwable e) {
                        logger.error("[SafeParaLoop][{}]", t, e);
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            try {
                return countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                return false;
            }
        }

        protected abstract void doRun0(T t) throws Exception;
    }
}
